/*
 * @Author: dengzhijun mento@dengzhijun
 * @Date: 2024-11-22
 * @LastEditors: dengzhijun mento@dengzhijun
 * @LastEditTime: 2024-12-11
 * @FilePath: /MenTo/EasyAOI/Common/Log/block_queue.h
 * @Description: 
 * Copyright (c) 2024 by Mento/dengzhijun, All Rights Reserved. 
 */
#ifndef TOOLS_BLOCKQUEUE_H_
#define TOOLS_BLOCKQUEUE_H_

#include <mutex>
#include <vector>
#include <condition_variable>
#include <chrono>
#include <stdexcept>
#include <atomic>

namespace Tool
{

template <class T>
class BlockQueue
{
public:
    explicit BlockQueue(size_t maxSize = 1000)
        : m_maxSize(maxSize), m_start(0), m_end(0), m_size(0), m_array(maxSize)
    {
        if (m_array.empty())
        {
            throw std::runtime_error("Failed to allocate memory for queue");
        }
    }

    BlockQueue(const BlockQueue&) = delete;
    BlockQueue& operator=(const BlockQueue&) = delete;

    ~BlockQueue()
    {
        Stop();
    }

    BlockQueue(BlockQueue&& other) noexcept
    {
        // std::lock_guard<std::mutex> lock(other.m_mutex);
        m_array = std::move(other.m_array);
        m_maxSize = other.m_maxSize;
        m_start = other.m_start;
        m_end = other.m_end;
        m_size = other.m_size;
        m_running = other.m_running;
    }

    BlockQueue& operator=(BlockQueue&& other) noexcept(
    std::is_nothrow_move_constructible<BlockQueue>::value &&    //不抛异常地移动构造（is_nothrow_move_constructible）
    std::is_nothrow_move_assignable<BlockQueue>::value          //不抛异常地移动赋值（is_nothrow_move_assignable）
    ) 
    {
        std::swap(m_array, other.m_array);
        m_maxSize = other.m_maxSize;
        m_start = other.m_start;
        m_end = other.m_end;
        m_size = other.m_size;
        m_running = other.m_running;
        return *this;
    }

    void Clear()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_start = m_end = m_size = 0;
    }

    bool IsEmpty()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return m_size == 0;
    }

    bool IsFull()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return m_size >= m_maxSize;
    }

    //原子操作只能保证单个变量原子性，所以得加锁。push和pop都会进行写操作所以使用互斥锁，而非读写锁
    void Push(const T &item)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_isPull.wait(lock, [this]{ return !m_running || m_size < m_maxSize; });
        if (!m_running)
        {
            return;
        }
        m_array[m_end] = item;
        m_end = (m_end + 1) % m_maxSize;
        ++m_size;
        m_isEmpty.notify_all();
    }

    void Push(T&& item)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_isPull.wait(lock, [this]{ return !m_running || m_size < m_maxSize; });
        if (!m_running)
        {
            return;
        }
        m_array[m_end] = std::move(item);
        m_end = (m_end + 1) % m_maxSize;
        ++m_size;
        m_isEmpty.notify_all();
    }

    //就地构造，零拷贝
    T Pop()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_isEmpty.wait(lock, [this] { return !m_running || m_size > 0; }); // 当m_isEmpty被触发时，等待直到队列非空或停止
        if (!m_running && !m_size)
        {
            return T();
        }
        T item = std::move(m_array[m_start]);
        m_start = (m_start + 1) % m_maxSize;
        --m_size;
        m_isPull.notify_all();
        return item;
    }

    bool TryPop(T& item, std::chrono::milliseconds timeout)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        if (!m_isEmpty.wait_for(lock, timeout, [this] { return !m_running || m_size > 0; }))
        {
            return false;
        }
        if (!m_running && !m_size)
        {
            return false;
        }
        item = std::move(m_array[m_start]);
        m_start = (m_start + 1) % m_maxSize;
        --m_size;
        m_isPull.notify_all();
        return true;
    }

    void Stop()
    {
        m_running = false;
        m_isPull.notify_all();
        m_isEmpty.notify_all();
    }

    bool Running() const
    {
        return m_running;
    }

    size_t Size()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return m_size;
    }

private:
    std::vector<T> m_array;
    size_t m_maxSize;
    size_t m_start;     //std::atomic_size_t
    size_t m_end;
    size_t m_size;
    std::mutex m_mutex;
    std::condition_variable m_isPull;
    std::condition_variable m_isEmpty;
    std::atomic_bool m_running = true;
};

}
#endif
